using System;
using System.Reflection;
using System.Text.RegularExpressions;
using NUnit.Framework;


#if (!SILVERLIGHT && !WindowsCE)

#endif

namespace ValidationFramework.Specifications.Tests
{
	[TestFixture]
	public sealed class MatchesRegexSpecTests
	{
		const string expectedRegularExpression = @"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";
		const RegexOptions expectedRegexOptions = RegexOptions.IgnorePatternWhitespace;


		[TestFixture]
		public class Constructor1
		{
			[Test]
			public void Should_Set_Regex_Expression()
			{
				// Arrange
				const string expectedRegularExpression = @"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";

				// Act
				var validator = new MatchesRegexSpec(expectedRegularExpression);

				// Assert
				Assert.AreEqual(expectedRegularExpression, validator.ValidationExpression);
			}

			[Test]
			public void Should_Default_Regex_Options_To_None()
			{
				// Arrange
				const string expectedRegularExpression = @"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";

				// Act
				var validator = new MatchesRegexSpec(expectedRegularExpression);

				// Assert
				Assert.AreEqual(expectedRegularExpression, validator.ValidationExpression);
				Assert.AreEqual(RegexOptions.None, validator.RegexOptions);
			}

			[Test]
			[ExpectedException(typeof(ArgumentNullException), ExpectedMessage = "Value cannot be null.\r\nParameter name: validationExpression")]
			public void Should_Throw_When_Given_A_Null_Expression()
			{
				new MatchesRegexSpec(null);
			}

			[Test]
			[ExpectedException(typeof(ArgumentException), ExpectedMessage = "String cannot be empty.\r\nParameter name: validationExpression")]
			public void Should_Throw_When_Given_An_Empty_Regex_Expression()
			{
				new MatchesRegexSpec(string.Empty);
			}
		}
		[TestFixture]
		public class Constructor2
		{
			[Test]
			public void Simple()
			{
				// Arrange
				const string expectedRegularExpression = @"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";
				const RegexOptions expectedRegexOptions = RegexOptions.IgnorePatternWhitespace;

				// Act
				var validator = new MatchesRegexSpec(expectedRegularExpression, expectedRegexOptions);

				// Assert
				Assert.AreEqual(expectedRegularExpression, validator.ValidationExpression);
				Assert.AreEqual(expectedRegexOptions, validator.RegexOptions);
			}

			[Test]
			[ExpectedException(typeof(ArgumentNullException), ExpectedMessage = "Value cannot be null.\r\nParameter name: validationExpression")]
			public void Should_Throw_When_Given_A_Null_Expression()
			{
				new MatchesRegexSpec(null);
			}

			[Test]
			[ExpectedException(typeof(ArgumentException), ExpectedMessage = "String cannot be empty.\r\nParameter name: validationExpression")]
			public void Should_Throw_When_Given_An_Empty_Regex_Expression()
			{
				new MatchesRegexSpec(string.Empty);
			}

			[Test]
			public void Should_Allow_Multiple_Regex_Options()
			{
				// Arrange
				const string regex = @"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";
#if (SILVERLIGHT)
                const RegexOptions optionRange = RegexOptions.CultureInvariant |
                                                             RegexOptions.ECMAScript |
                                                             RegexOptions.ExplicitCapture |
                                                             RegexOptions.IgnoreCase |
                                                             RegexOptions.IgnorePatternWhitespace |
                                                             RegexOptions.Multiline |
                                                             RegexOptions.None |
                                                             RegexOptions.RightToLeft |
                                                             RegexOptions.Singleline;
#else

				const RegexOptions optionRange = RegexOptions.Compiled |
															 RegexOptions.CultureInvariant |
															 RegexOptions.ECMAScript |
															 RegexOptions.ExplicitCapture |
															 RegexOptions.IgnoreCase |
															 RegexOptions.IgnorePatternWhitespace |
															 RegexOptions.Multiline |
															 RegexOptions.None |
															 RegexOptions.RightToLeft |
															 RegexOptions.Singleline;
#endif

				// Act
				var validator2 = new MatchesRegexSpec(regex, optionRange);

				// Assert
				Assert.AreEqual(validator2.RegexOptions, optionRange);

			}

			[Test]
			public void Should_Allow_Single_Regex_Option()
			{
				// Arrange
				const string regex = @"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";

				// Act
				var validator2 = new MatchesRegexSpec(regex, RegexOptions.Singleline);

				// Assert
				Assert.AreEqual(validator2.RegexOptions, RegexOptions.Singleline);

			}

			[Test]
			public void Should_Throw_When_Given_Invalid_RegexOptions()
			{
				Exception argException = null;
				try
				{
					var constructor = typeof(MatchesRegexSpec).GetConstructor(new[] { typeof(string), typeof(RegexOptions) });
					constructor.Invoke(new object[] { expectedRegularExpression, -1 });
				}
				catch (TargetInvocationException e)
				{
					argException = e.InnerException;
				}
				Assert.IsNotNull(argException);
			}
		}

		[TestFixture]
		public class IsEquivalent
		{
			[Test]
			public void Simple()
			{
				const string simpleRegx = "regex";
				var validator1 = new MatchesRegexSpec(simpleRegx, RegexOptions.None);
				var validator2 = new MatchesRegexSpec(simpleRegx, RegexOptions.None);
				Assert.IsTrue(validator1.IsEquivalentTo(validator2));

				validator1 = new MatchesRegexSpec(simpleRegx, RegexOptions.None);
				validator2 = new MatchesRegexSpec(simpleRegx + "2", RegexOptions.None);
				Assert.IsFalse(validator1.IsEquivalentTo(validator2));
			}
		}

		[TestFixture]
		public class Validate
		{
			[Test]
			public void Simple()
			{
				var emailRegex = @"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";
				var validator = new MatchesRegexSpec(emailRegex, RegexOptions.None);

				Assert.IsTrue(validator.IsSatisfiedBy(null));
				Assert.IsTrue(validator.IsSatisfiedBy(string.Empty));
				Assert.IsFalse(validator.IsSatisfiedBy("aaa"));
				Assert.IsTrue(validator.IsSatisfiedBy("a@b.com"));
			}
		}

		// Moved Web Client Test to ValidationFramework.Web.Tests


		[TestFixture]
		public class CheckType
		{

			[Test]
			public void Should_Return_True_For_Allowed_Types()
			{
				var validator = new MatchesRegexSpec("sss", RegexOptions.None);
				Assert.IsTrue(validator.CanBeSatisfiedBy(typeof(string)));
			}

			[Test]
			public void Should_Return_False_For_Invalid_Types()
			{
				var validator = new MatchesRegexSpec("sss", RegexOptions.None);
				Assert.IsFalse(validator.CanBeSatisfiedBy(typeof(int)));
			}

			//[Test]
			////[ExpectedException(typeof(ArgumentException), ExpectedMessage = "Member 'foo' must be a 'System.String' to be used for the ValidationFramework.RegexRule. Actual Type 'System.Int32'.\r\nParameter name: value")]
			//public void Should_Throw_Exception_For_Invalid_Types()
			//{
			//    var validator = new MatchesRegexSpec("sss", RegexOptions.None);
			//    validator.CanBeSatisfiedBy(typeof(int));
			//}
		}
	}
}